﻿using System;
using UnityEngine;
using System.Collections;
using YooAsset;

public class PatchOperation : GameAsyncOperation
{
    private enum ESteps
    {
        None,
        Update,
        Done,
    }

    private readonly string _packageName;
    private readonly EPlayMode _playMode;
    private ESteps _steps = ESteps.None;

    private ResourcePackage _package;
    private ServerConfig _serverConfig;

    private RequestPackageVersionOperation _requestPackageVersionOperation;
    private UpdatePackageManifestOperation _updatePackageManifestOperation;
    private ResourceDownloaderOperation _downloader;


    public PatchOperation(string packageName, EPlayMode playMode)
    {
        _packageName = packageName;
        _playMode = playMode;

        GameEvent.OnStartInitialize += StartInitialize;
        GameEvent.OnTryPackageVersion += TryUpdatePackageVersion;
        GameEvent.OnTryPackageManifest += TryUpdatePackageManifest;
        GameEvent.OnTryDownload += TryDownload;
        GameEvent.OnDownloadOver += ClearCache;
    }

    protected override void OnStart()
    {
        _steps = ESteps.Update;
        // 【步骤1】创建并初始化资源包
        StartInitialize();
    }

    protected override void OnUpdate()
    {
        if (_steps is ESteps.None or ESteps.Done) return;
    }

    protected override void OnAbort()
    {
    }

    private void SetFinish()
    {
        _steps = ESteps.Done;
        GameEvent.OnStartInitialize -= StartInitialize;
        GameEvent.OnTryPackageVersion -= TryUpdatePackageVersion;
        GameEvent.OnTryPackageManifest -= TryUpdatePackageManifest;
        GameEvent.OnTryDownload -= TryDownload;
        GameEvent.OnDownloadOver -= ClearCache;
        Status = EOperationStatus.Succeed;
        Debug.Log($"Package {_packageName} patch done !");
    }

    #region 事件响应

    private void StartInitialize()
    {
        // 【步骤1】创建并初始化资源包
        PatchManager.Instance.StartCoroutine(InitializeResourcePackageCoroutine());
    }


    private void TryUpdatePackageVersion()
    {
        // 【步骤2】请求资源版本
        PatchManager.Instance.StartCoroutine(UpdatePackageVersion());
    }

    private void TryUpdatePackageManifest()
    {
        // 【步骤3】请求资源清单
        PatchManager.Instance.StartCoroutine(UpdateManifest());
    }


    private void TryDownload()
    {
        // 【步骤5】开始下载资源文件
        PatchManager.Instance.StartCoroutine(BeginDownload());
    }

    private void ClearCache()
    {
        // 【步骤6】清理未使用的缓存文件
        Debug.Log($"[YooAsset] 清理未使用的缓存文件");
        GameEvent.SendPatchStepsChange(PatchStepDefine.ClearCache);
        _package = YooAssets.GetPackage(_packageName);
        var operation = _package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
        operation.Completed += Operation_Completed;
    }

    private void Operation_Completed(AsyncOperationBase obj)
    {
        Debug.Log($"[YooAsset] 开始游戏");
        GameEvent.SendPatchStepsChange(PatchStepDefine.StartGame);
        SetFinish();
    }

    #endregion

    #region 分步骤流程

    private IEnumerator InitializeResourcePackageCoroutine()
    {
        Debug.Log($"[YooAsset] 正在初始化资源包，模式: {_playMode}");
        GameEvent.SendPatchStepsChange(PatchStepDefine.Initialize);
        _package = YooAssets.TryGetPackage(_packageName) ?? YooAssets.CreatePackage(_packageName);

        InitializationOperation initializationOperation;

        switch (_playMode)
        {
            case EPlayMode.EditorSimulateMode:
            {
                var buildResult = EditorSimulateModeHelper.SimulateBuild(_packageName);
                var packageRoot = buildResult.PackageRootDirectory;
                var createParameters = new EditorSimulateModeParameters
                {
                    EditorFileSystemParameters =
                        FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot)
                };
                initializationOperation = _package.InitializeAsync(createParameters);
                break;
            }
            // 单机运行模式
            case EPlayMode.OfflinePlayMode:
            {
                var fileDecryption = new SimpleFileStreamDecryption();
                var manifestServices = new SimpleRestoreManifest();
                var createParameters = new OfflinePlayModeParameters
                {
                    BuildinFileSystemParameters =
                        FileSystemParameters.CreateDefaultBuildinFileSystemParameters(fileDecryption)
                };
                createParameters.BuildinFileSystemParameters.AddParameter(
                    FileSystemParametersDefine.DISABLE_CATALOG_FILE, true);
                createParameters.BuildinFileSystemParameters.AddParameter(FileSystemParametersDefine.MANIFEST_SERVICES,
                    manifestServices);
                initializationOperation = _package.InitializeAsync(createParameters);
                break;
            }
            // 联机运行模式
            case EPlayMode.HostPlayMode:
            {
                var defaultHostServer = GetHostServerURL();
                var fallbackHostServer = GetHostServerURL();
                var fileDecryption = new SimpleFileStreamDecryption();
                var manifestServices = new SimpleRestoreManifest();
                IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
                var createParameters = new HostPlayModeParameters
                {
                    BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(fileDecryption),
                    CacheFileSystemParameters =
                        FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices, fileDecryption)
                };
                createParameters.CacheFileSystemParameters.AddParameter(FileSystemParametersDefine.MANIFEST_SERVICES,
                    manifestServices);
                initializationOperation = _package.InitializeAsync(createParameters);
                break;
            }
            // WebGL运行模式
            case EPlayMode.WebPlayMode:
            {
#if UNITY_WEBGL && WEIXINMINIGAME && !UNITY_EDITOR
            var createParameters = new WebPlayModeParameters();
			string defaultHostServer = GetHostServerURL();
            string fallbackHostServer = GetHostServerURL();
            string packageRoot = $"{WeChatWASM.WX.env.USER_DATA_PATH}/__GAME_FILE_CACHE"; //注意：如果有子目录，请修改此处！
            IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
            createParameters.WebServerFileSystemParameters =
 WechatFileSystemCreater.CreateFileSystemParameters(packageRoot, remoteServices);
            initializationOperation = package.InitializeAsync(createParameters);
#else
                var createParameters = new WebPlayModeParameters
                {
                    WebServerFileSystemParameters = FileSystemParameters.CreateDefaultWebServerFileSystemParameters()
                };
                initializationOperation = _package.InitializeAsync(createParameters);
#endif
                break;
            }
            default:
                throw new ArgumentOutOfRangeException();
        }

        yield return initializationOperation;

        if (initializationOperation != null && initializationOperation.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning($"[YooAsset] 资源包初始化失败: {initializationOperation.Error}");
            GameEvent.SendInitializeFailed();
        }
        else
        {
            Debug.Log("[YooAsset] 资源包初始化成功！");
            TryUpdatePackageVersion();
        }
    }


    private IEnumerator UpdatePackageVersion()
    {
        Debug.Log($"[YooAsset] 正在请求资源版本");
        GameEvent.SendPatchStepsChange(PatchStepDefine.Version);
        _package = YooAssets.GetPackage(_packageName);
        _requestPackageVersionOperation = _package.RequestPackageVersionAsync();
        yield return _requestPackageVersionOperation;

        if (_requestPackageVersionOperation.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning($"[YooAsset] 请求版本失败: {_requestPackageVersionOperation.Error}");
            GameEvent.SendPackageVersionRequestFailed();
        }
        else
        {
            Debug.Log($"[YooAsset] 请求版本成功 : {_requestPackageVersionOperation.PackageVersion}");

            TryUpdatePackageManifest();
        }
    }

    private IEnumerator UpdateManifest()
    {
        // 【步骤3】请求资源清单
        Debug.Log($"[YooAsset] 正在更新Manifest");
        GameEvent.SendPatchStepsChange(PatchStepDefine.Manifest);
        _package = YooAssets.GetPackage(_packageName);
        _updatePackageManifestOperation =
            _package.UpdatePackageManifestAsync(_requestPackageVersionOperation.PackageVersion);
        yield return _updatePackageManifestOperation;

        if (_updatePackageManifestOperation.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning($"[YooAsset] Manifest更新失败: {_updatePackageManifestOperation.Error}");
            GameEvent.SendPackageManifestUpdateFailed();
        }
        else
        {
            Debug.Log("[YooAsset] Manifest更新成功！");
            // 【步骤4】创建资源下载器
            CreateDownloader();
        }
    }

    void CreateDownloader()
    {
        // 【步骤4】创建资源下载器
        Debug.Log($"[YooAsset] 正在创建资源下载器");
        GameEvent.SendPatchStepsChange(PatchStepDefine.CreateDownloader);
        _package = YooAssets.GetPackage(_packageName);
        const int downloadingMaxNum = 10;
        const int failedTryAgain = 3;
        _downloader = _package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);

        if (_downloader.TotalDownloadCount == 0)
        {
            Debug.Log("Not found any download files !");
            GameEvent.SendPatchStepsChange(PatchStepDefine.StartGame);
            SetFinish();
        }
        else
        {
            // 发现新更新文件后，挂起流程系统
            // 注意：开发者需要在下载前检测磁盘空间不足
            var totalDownloadCount = _downloader.TotalDownloadCount;
            var totalDownloadBytes = _downloader.TotalDownloadBytes;
            GameEvent.SendFoundUpdateFiles(totalDownloadCount, totalDownloadBytes);

            var sizeMb = totalDownloadBytes / 1048576f;
            sizeMb = Mathf.Clamp(sizeMb, 0.1f, float.MaxValue);
            var totalSizeMb = sizeMb.ToString("f1");
            Debug.Log($"[YooAsset] 发现新更新文件 Total count {totalDownloadCount} Total size {totalSizeMb}MB！");
        }
    }


    private IEnumerator BeginDownload()
    {
        Debug.Log($"[YooAsset] 开始下载资源文件");
        GameEvent.SendPatchStepsChange(PatchStepDefine.Download);
        _downloader.DownloadErrorCallback = GameEvent.SendWebFileDownloadFailed;
        _downloader.DownloadUpdateCallback = GameEvent.SendDownloadUpdate;
        _downloader.BeginDownload();
        yield return _downloader;

        // 检测下载结果
        if (_downloader.Status != EOperationStatus.Succeed)
            yield break;

        GameEvent.SendPatchStepsChange(PatchStepDefine.DownloadOver);
    }




    #endregion

    #region 辅助方法

    /// <summary>
    /// 获取资源服务器地址
    /// </summary>
    private static string GetHostServerURL()
    {
        //string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
        const string hostServerIP = "http://127.0.0.1:8888";
        const string appVersion = "v1.0";

#if UNITY_EDITOR
        return UnityEditor.EditorUserBuildSettings.activeBuildTarget switch
        {
            UnityEditor.BuildTarget.Android => $"{hostServerIP}/CDN/Android/{appVersion}",
            UnityEditor.BuildTarget.iOS => $"{hostServerIP}/CDN/IPhone/{appVersion}",
            UnityEditor.BuildTarget.WebGL => $"{hostServerIP}/CDN/WebGL/{appVersion}",
            _ => $"{hostServerIP}/CDN/PC/{appVersion}"
        };
#else
        return Application.platform switch
        {
            RuntimePlatform.Android => $"{hostServerIP}/CDN/Android/{appVersion}",
            RuntimePlatform.IPhonePlayer => $"{hostServerIP}/CDN/IPhone/{appVersion}",
            RuntimePlatform.WebGLPlayer => $"{hostServerIP}/CDN/WebGL/{appVersion}",
            _ => $"{hostServerIP}/CDN/PC/{appVersion}"
        };
#endif
    }

    /// <summary>
    /// 远端资源地址查询服务类
    /// </summary>
    private class RemoteServices : IRemoteServices
    {
        private readonly string _defaultHostServer;
        private readonly string _fallbackHostServer;

        public RemoteServices(string defaultHostServer, string fallbackHostServer)
        {
            _defaultHostServer = defaultHostServer;
            _fallbackHostServer = fallbackHostServer;
        }

        string IRemoteServices.GetRemoteMainURL(string fileName)
        {
            return $"{_defaultHostServer}/{fileName}";
        }

        string IRemoteServices.GetRemoteFallbackURL(string fileName)
        {
            return $"{_fallbackHostServer}/{fileName}";
        }
    }

    [Serializable]
    public class ServerConfig
    {
        public string hostServerIP;
        public string appVersion;
    }
    

    #endregion
}